
/************************************************************************

								Heroes IV
					  Copyright 2001, The 3DO Company

	------------------------------------------------------------------
  						 ai_combat_data_cache.h

	$Header:  $

	$NoKeywords: $

 ************************************************************************/

#if !defined( AI_COMBAT_DATA_CACHE_H_INCLUDED )
#define AI_COMBAT_DATA_CACHE_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <map>
#include "area_effect_point_set.h"
#include "area_effect_shape.h"
#include "attack_angle.h"
#include "combat_creature_list.h"
#include "combat_path_finder.h"
#include "combat_terrain_map.h"
#include "creature_int_map.h"
#include "map_point.h"
#include "minimum_maximum.h"
#include "owned_ptr.h"
#include "spell.h"
#include "uncopyable.h"


class t_attackable_object;
class t_combat_creature;

typedef t_isometric_map<double>							t_influence_map;

double get_total_weight( t_creature_int_map const& damage_map, bool side );

// The t_ai_combat_data_cache class stores various info that the AI needs 
// during its calculations. The cache ensures that certain often needed pieces
// of data are calculated when needed but not more that needed
class t_ai_combat_data_cache : private t_uncopyable
{
public:
	t_ai_combat_data_cache();

	//// Functions to retrieve cached data /////
	void						clear_paths();
	void						clear_terrain_maps();
	// Get approximate battle length, in turns
	int							get_approximate_battle_length(void);
	int							get_approximate_first_casualty_time( t_combat_creature const& creature );
	int							get_approximate_lifespan(const t_combat_creature &creature);
	int							get_approximate_losses( t_combat_creature const& creature );
	t_attack_angle_list const&	get_attack_angles( t_combat_creature const&		attacker, 
												   t_attackable_object const&	defender,
												   bool							ignore_friendly = false );
	bool						get_attack_time( t_combat_creature const&	attacker,
												 t_combat_creature const&	target,
												 int&						turns );
	// Average damage per hitpoint for a side
	double get_average_damage_per_hitpoint(bool for_defender);
	// together with the creatures affected by the spell/attack. Ignores anything
	// specific to a certain caster / attacker, such as LOS, can_affect, etc.
	const t_area_effect_point_set&	get_base_area_effect_point_set(t_area_effect_shape shape);
	// Get ranged units this unit is interfering with
	const t_combat_creature_list&	get_creature_interferes_with_list( t_combat_creature const& creature);
	double							get_influence_weight( t_map_point_2d const& point );
	// Get units which are interfering with this unit
	const t_combat_creature_list&	get_interferes_with_creature_list( t_combat_creature const& creature);
	// get path obstacle map for a given footprint
	t_combat_path_map const&		get_obstacle_map( int size );
	double							get_retaliation_weight_reduction( t_combat_creature const& target,
																	  bool					   ranged );
	t_combat_terrain_map const&		get_terrain_map( int footprint_size );
	// Get the basic list of points on which an area effect spell/attack can be cast,
	// Total combat value of a side
	double							get_total_combat_value(bool for_defender);
	void							initialize( t_battlefield& battlefield );
	void							new_action(void); // Previous action completed, prepare cache for new action
	bool							spellcasting_is_worthwhile( bool side );

private:
	struct t_turns_to_combat_key
	{
		const t_combat_creature * creature;
		bool					  count_incoming_ranged;
		bool					  count_outgoing_ranged;
	};

	struct t_turns_to_combat_predicate
	{
		bool operator()(const t_turns_to_combat_key &key1, const t_turns_to_combat_key &key2);
	};

	struct t_best_damage_data
	{
		int best_ranged;
		int best_melee;
	};

	struct t_attacker_defender_key
	{
		t_combat_creature const*	attacker;
		t_attackable_object const*	defender;
		bool						ignore_friendly;

		bool operator < ( t_attacker_defender_key const& arg ) const;
	};

	struct t_creature_effect_key
	{
		t_combat_creature const* creature;
		t_spell                  effect;
	};

	struct t_creature_effect_predicate
	{
		bool operator()(const t_creature_effect_key &key1, const t_creature_effect_key &key2);
	};

	typedef std::map<t_attacker_defender_key, 
					 t_attack_angle_list >				t_attack_angles_map;
	typedef t_owned_ptr<t_combat_path_blockage_map>		t_blockage_ptr;
	typedef std::map<int, t_combat_path_map>			t_path_map_keyed_array;
	typedef std::map<const t_combat_creature*, 
					 t_combat_creature_list>			t_creature_creature_list_map;
	typedef std::map<const t_combat_creature*, 
					 t_best_damage_data>				t_creature_damage_map;
	typedef std::map<const t_combat_creature*, 
					 double>							t_creature_float_map;
	typedef std::map<t_creature_effect_key,
					 t_best_damage_data, 
					 t_creature_effect_predicate>		t_creature_effect_damage_map;
	typedef std::map< int, t_combat_terrain_map >		t_terrain_keyed_array;
	typedef std::map<t_turns_to_combat_key, int, 
					 t_turns_to_combat_predicate>		t_turns_to_combat_map;


	int								m_approximate_battle_length; // -1 for not known 
	t_area_effect_point_set			m_area_effect_point_sets[k_num_area_effect_shapes];  // Base list (without stage 2 filtering) of points useable for a given shape
	t_attack_angles_map				m_attack_angles_map;		  // Attack angles for attacker / defender pair
	double							m_average_damage_per_hitpoint[2]; // Average damage per hitpoint of creatures on a side
	t_battlefield*					m_battlefield;
	t_creature_damage_map			m_best_damage_map;			  // Map of creatures to the highest average damage they can do from an attack
	t_creature_effect_damage_map	m_best_damage_with_effect_map; // Map of creatures to the highest average damage they could do from an attack if a certain effect was turned on
	t_creature_effect_damage_map	m_best_damage_without_effect_map; // Map of creatures to the highest average damage they could do from an attack if a certain effect was turned off
	int								m_best_defense_bonus[2];     // Best defensive bonus for each side. -1 for not known
	int								m_best_offense_bonus[2];     // Best offensive bonus for each side. -1 for not known
	int								m_casualties_without_spellcasting[2];
	t_creature_creature_list_map	m_creature_interferes_with_list_map; // Map of creatures to list of creatures key creature is interfering with 
	t_influence_map					m_influence_map;
	bool							m_influence_map_valid;
	t_creature_creature_list_map	m_interferes_with_creature_list_map; // Map of creatures to list of creatures which are interfering with key creature
	t_blockage_ptr					m_obstacle_map_ptr;
	t_creature_int_map				m_optimistic_damage_map;     // Map of creatures to the highest average damage they can do from an attack in a best case senerio
	t_path_map_keyed_array			m_path_maps;
	bool							m_retaliation_weight_reduction_valid[2][2];
	t_terrain_keyed_array			m_terrain_maps;
	double							m_total_combat_value[2];     // Total combat value of all creatures on a side
	t_turns_to_combat_map			m_turns_to_combat_map;		  // Map of creatures to turns to combat

	// Functions to calculate / generate data in cache
	void							calculate_approximate_battle_length(void);
	void							compute_retaliation_weight_reductions( bool side, bool ranged );
	void							create_influence_map();
	void							generate_attack_angles(const t_combat_creature&		attacker, 
														   const t_attackable_object&	defender,
														   t_combat_path_finder&		pathfinder,
														   t_attack_angle_list&			result);
	void							generate_base_area_effect_point_set(t_area_effect_shape shape, 
																		t_area_effect_point_set &result);
	void							generate_creatures_area_effect_point_set(
										t_area_effect_shape			shape, 
										const t_combat_creature&	creature, 
										t_area_effect_point_set&	result);
	void							generate_total_combat_values(void);
};

inline void t_ai_combat_data_cache::clear_terrain_maps()
{
	m_terrain_maps.clear();
}

inline 
int t_ai_combat_data_cache::get_approximate_battle_length(void)
{
	if (m_approximate_battle_length == -1)
	{
		calculate_approximate_battle_length();
	}

	return m_approximate_battle_length;
}


// Get the basic list of points on which an area effect spell/attack can be cast,
// together with the creatures affected by the spell/attack. Ignores LOS,
// can_affect, etc.
inline
const t_area_effect_point_set &t_ai_combat_data_cache::get_base_area_effect_point_set(t_area_effect_shape shape)
{
	assert(shape >= 0);
	assert(shape < k_num_area_effect_shapes);

	if (m_area_effect_point_sets[shape].empty())
	{
		generate_base_area_effect_point_set(shape, m_area_effect_point_sets[shape]);
	}

	return m_area_effect_point_sets[shape];
}

inline
double t_ai_combat_data_cache::get_total_combat_value(bool for_defender)
{
	if (m_total_combat_value[0] < 0.0)
		generate_total_combat_values();

	return m_total_combat_value[for_defender];
}

//---------------------------------------------------------
// Predicates for maps with complex (multi-variable) keys
// For all functions: We don't really care about ordering,
// we just want a uniqueness test
inline
bool t_ai_combat_data_cache::t_turns_to_combat_predicate::operator()(const t_turns_to_combat_key &key1, const t_turns_to_combat_key &key2)
{
	if (key1.creature < key2.creature)
		return true;
	if (key1.creature > key2.creature)
		return false;

	if (key1.count_incoming_ranged && !key2.count_incoming_ranged)
		return true;

	if (!key1.count_incoming_ranged && key2.count_incoming_ranged)
		return false;

	if (key1.count_outgoing_ranged && !key2.count_outgoing_ranged)
		return true;

	// redundant
	//if (!key1.count_outgoing_ranged && key2.count_outgoing_ranged)
	//	return false;

	return false; // Identical
}

inline
bool t_ai_combat_data_cache::t_attacker_defender_key::operator < ( t_attacker_defender_key const& arg ) const
{
	if (defender < arg.defender)
		return true;

	if (defender > arg.defender)
		return false;

	if (attacker < arg.attacker)
		return true;
	if (attacker > arg.attacker)
		return true;
	return ignore_friendly < arg.ignore_friendly;
}


inline
bool t_ai_combat_data_cache::t_creature_effect_predicate::operator()(const t_creature_effect_key &key1, const t_creature_effect_key &key2)
{
	if (key1.effect < key2.effect)
		return true;

	if (key1.effect > key2.effect)
		return false;

	return key1.creature < key2.creature;
}

#endif  //AI_COMBAT_DATA_CACHE_H_INCLUDED

